/** ----------------------------------------------------------
 * \class VSShaderLib
 *
 * Lighthouse3D
 *
 * VSShaderLib - Very Simple Shader Library
 *
 * Full documentation at 
 * http://www.lighthouse3d.com/very-simple-libs
 *
 * This class aims at making life simpler
 * when using shaders and uniforms
 *
 * version 0.2.2
 *		Added image load store types
 *
 * version 0.2.1
 *		Added more attrib defs, namely
 *			tangents, bi tangents, and 4 custom
 * 
 * version 0.2.0
 *		Added methods to set uniforms
 *		Added methods to set blocks
 *		Renamed to VSShaderLib
 *
 * version 0.1.0 
 * Initial Release
 *
 * This lib requires:
 *
 * GLEW (http://glew.sourceforge.net/)
 *
 ---------------------------------------------------------------*/


#ifndef __VSShaderLib__
#define __VSShaderLib__

#include <string>
#include <vector>
#include <map>
#include <GL/glew.h>


class VSShaderLib
{
public:
	
	/// Types of Vertex Attributes
	enum AttribType {
		VERTEX_COORD_ATTRIB,
		NORMAL_ATTRIB,
		TEXTURE_COORD_ATTRIB,
		TANGENT_ATTRIB,
		BITANGENT_ATTRIB,
		VERTEX_ATTRIB1,
		VERTEX_ATTRIB2,
		VERTEX_ATTRIB3,
		VERTEX_ATTRIB4
	};

	/// Types of Shaders
	enum ShaderType {
		VERTEX_SHADER,
		GEOMETRY_SHADER,
		TESS_CONTROL_SHADER,
		TESS_EVAL_SHADER,
		FRAGMENT_SHADER,
		COMPUTE_SHADER,
		COUNT_SHADER_TYPE
	};

	/// Just a helper define
	static const int MAX_TEXTURES = 8;

	VSShaderLib();
	~VSShaderLib();

	/** Init should be called for every shader instance
	  * prior to any other function
	*/
	void init();
	
	/** Loads the text in the file to the source of the specified shader 
	  *
	  * \param st one of the enum values of ShaderType
	  *	\param filename the file where the source is to be found
	*/
	void loadShader(VSShaderLib::ShaderType st, std::string fileName);

	/** bind a user-defined varying out variable to a 
	  * fragment shader color number
	  * Note: linking is required for this operation to take effect
	  * (call method prepareProgram afterwards)
	  *
	  * \param index the fragment colour number
	  * \param the name of the fragment's shader variable
	*/
	void setProgramOutput(int index, std::string name);

	/** returns the fragment shader color number bound to 
	  * a user-defined varying out variable
	  * 
	  * Note: linking is required for this operation to take effect
	  * (call method prepareProgram afterwards)
	  *
	  * \param the name of the fragment's shader variable
	  * \returns the fragment colour number
	*/
	GLint getProgramOutput(std::string name);

	/** Defines semantics for the input vertex attributes. This is
	  * required for other libraries to know how to send data to the shader
	  * Note: linking is required for this operation to take effect
	  * (call method prepareProgram)
	  *
	  * \param the semantic of the attribute
	  * \param the name of the vertex attribute
	*/
	void setVertexAttribName(VSShaderLib::AttribType at, std::string name);

	/** Prepares program for usage. Links it and collects information
	  * about uniform variables and uniform blocks
	*/
	void prepareProgram();

	/// generic function to set the uniform <name> to value
	void setUniform(std::string name, void *value);
	/// For int and bool uniforms. Sets the uniform <name> to the int value
	void setUniform(std::string name, int value);
	/// For float uniforms. Sets the uniform <name> to the float value
	void setUniform(std::string name, float value);
	/// sets a uniform block as a whole
	static void setBlock(std::string name, void *value);
	/// sets a uniform inside a named block
	static void setBlockUniform(std::string blockName, 
								std::string uniformName, 
								void *value);
	/// sets an element of an array of uniforms inside a block
	static void setBlockUniformArrayElement(std::string blockName, 
								std::string uniformName,
								int arrayIndex, 
								void * value);

	/// returns the program index
	GLuint getProgramIndex();
	/// returns a shader index
	GLuint getShaderIndex(VSShaderLib::ShaderType);

	/// returns a string with a shader's infolog
	std::string getShaderInfoLog(VSShaderLib::ShaderType);
	/// returns a string with the program's infolog
	std::string getProgramInfoLog();
	/// returns a string will all info logs
	std::string getAllInfoLogs();
	/// returns GL_VALIDATE_STATUS for the program 
	bool isProgramValid();
	/// returns true if compiled, false otherwise
	bool isShaderCompiled(VSShaderLib::ShaderType);
	/// returns true if linked, false otherwise
	bool isProgramLinked();


protected:

	// AUX STRUCTURES

	/// stores information for uniforms
	typedef struct uniforms {
		GLenum type;
		GLuint location;
		GLuint size;
		GLuint stride;
	}myUniforms;

	/// stores information for block uniforms
	typedef struct blockUniforms {
		GLenum type;
		GLuint offset;
		GLuint size;
		GLuint arrayStride;
	} myBlockUniform;

	/// stores information for a block and its uniforms
	class UniformBlock {

		public:
			/// size of the uniform block
			int size;
			/// buffer bound to the index point
			GLuint buffer;
			/// binding index
			GLuint bindingIndex;
			/// uniforms information
			std::map<std::string, myBlockUniform > uniformOffsets;
	};

	// VARIABLES

	/// stores if init has been called
	bool pInited;


	/// blockCount is used to assign binding indexes
	static int spBlockCount;

	/// Stores info on all blocks found
	static std::map<std::string, UniformBlock> spBlocks;

	/// stores the OpenGL shader types
	static GLenum spGLShaderTypes[VSShaderLib::COUNT_SHADER_TYPE];
	
	/// stores the text string related to each type
	static std::string spStringShaderTypes[VSShaderLib::COUNT_SHADER_TYPE];

	/// aux string used to return the shaders infologs
	std::string pResult;

	/// stores the shaders and program indices
	GLuint pShader[VSShaderLib::COUNT_SHADER_TYPE], pProgram;

	/// stores info on the uniforms
	std::map<std::string, myUniforms> pUniforms;

	// AUX FUNCTIONS

	/// aux function to get info on the uniforms referenced by the shaders
	void addUniforms();
	
	/// aux function to store the info of a uniform
	void addUniform(std::string name, GLenum type, unsigned int size);

	/// aux function to get info on the blocks referenced by the shaders
	void addBlocks();

	/// determines the size in bytes based on the OpenGL type
	int typeSize(int type);

	/// aux function to read the shader's source code from file
	char *textFileRead(std::string fileName);
};

	

#endif
